# -*- coding: utf-8 -*-

#   This file is part of emesene.
#
#    Emesene is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    emesene is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with emesene; if not, write to the Free Software
#    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

#    This plugin uses parts of code from youtube-dl from:
# 						Author: Ricardo Garcia Gonzalez
# 						Author: Danny Colligan
# 						Author: Benjamin Johnson

# General Use
import gtk
import Plugin
import re
from urlparse import urlparse
import paths

import subprocess
import desktop

# For Saving Video
import thread
import os

# For youtube-dl class
import httplib
import socket
import time
import urllib2

import gettext
_ = gettext.gettext

# For Webkit
import gobject

ERROR = ''

try:
    import gdata.youtube, gdata.youtube.service
    HAVE_GDATA = True

except:
    HAVE_GDATA = False

try:
    import webkit
    HAVE_WEBKIT = True
except:
    HAVE_WEBKIT = False

try:
    import gtkmozembed
    HAVE_GECKO = True
except:
    HAVE_GECKO = False

VERSION = '0.8.74'

YOUTUBE_VIDEO_URI = 'http://gdata.youtube.com/feeds/api/videos'

# constants from youtube-dl
CONST_1K = 1024
CONST_INITIAL_BLOCK_SIZE = 10 * CONST_1K
CONST_EPSILON = 0.0001
CONST_TIMEOUT = 120

CONST_VIDEO_URL_STR = 'http://www.youtube.com/watch?v=%s'
CONST_VIDEO_URL_RE = re.compile(r'^((?:http://)?(?:\w+\.)?youtube\.com/(?:v/|(?:watch(?:\.php)?)?\?(?:.+&)?v=))?([0-9A-Za-z_-]+)(?(1)[&/].*)?$')
CONST_LOGIN_URL_STR = 'http://www.youtube.com/login?next=/watch%%3Fv%%3D%s'
CONST_LOGIN_POST_STR = 'current_form=loginForm&next=%%2Fwatch%%3Fv%%3D%s&username=%s&password=%s&action_login=Log+In'
CONST_AGE_URL_STR = 'http://www.youtube.com/verify_age?next_url=/watch%%3Fv%%3D%s'
CONST_AGE_POST_STR = 'next_url=%%2Fwatch%%3Fv%%3D%s&action_confirm=Confirm'
CONST_URL_T_PARAM_RE = re.compile(r', "t": "([^"]+)"')
CONST_VIDEO_URL_REAL_STR = 'http://www.youtube.com/get_video?video_id=%s&t=%s'
CONST_VIDEO_TITLE_RE = re.compile(r'<title>YouTube - ([^<]*)</title>', re.M | re.I)

# Available ways to show the video
SHOW_MODE_INSIDE = _("See it in the conversation window")
SHOW_MODE_POPUP = _("See it in a popup window")
SHOW_MODE_WEB_BROWSER = _("See it in a web browser")
SHOW_MODE_ASK = _("Always ask me")
SHOW_MODE_SAVE = _("Just save it")
SHOW_MODE_NONE = _("Do nothing")
SHOW_MODES_POSIX = [ SHOW_MODE_INSIDE, SHOW_MODE_POPUP, SHOW_MODE_WEB_BROWSER, SHOW_MODE_ASK, SHOW_MODE_SAVE, SHOW_MODE_NONE ]
SHOW_MODES_NT = [ SHOW_MODE_WEB_BROWSER, SHOW_MODE_ASK, SHOW_MODE_SAVE, SHOW_MODE_NONE ]

BACKEND_WEBKIT = "Webkit"
BACKEND_GECKO = "Gecko"
BACKEND_BROWSER = _("Web browser")
BACKENDS = [ BACKEND_WEBKIT, BACKEND_GECKO, BACKEND_BROWSER ]

# Plugin info
PLUGIN_NAME = 'YAYoutube'
PLUGIN_DISPLAY_NAME = _('Yet another Youtube')
PLUGIN_DESCRIPTION = _('Opens youtube videos for easy viewing while chatting (can save them, too)\n\nPackages recomended:\npython-gdata and python-webkit or python-gtkmozembed')
PLUGIN_AUTHORS = { 'Gustau Castells (Karasu)' : 'gustaucastells@gmail.com',
                   'Mauro (Mauro22)' : 'm.de.mauro@gmail.com' }
PLUGIN_WEBSITE = 'code.google.com/p/yayoutube'

PLUGIN_DEBUG = 0

class MainClass(Plugin.Plugin):
    '''Main plugin class'''
    
    name = PLUGIN_NAME
    displayName = PLUGIN_DISPLAY_NAME
    description = PLUGIN_DESCRIPTION
    authors = PLUGIN_AUTHORS
    website = PLUGIN_WEBSITE
    
    def __init__( self, controller, msn ):
        '''Constructor, the period parameter is the time between calls to 
        the function, in milliseconds, if period = None then the plugin 
        is asynchronous.'''
        
        Plugin.Plugin.__init__( self, controller, msn, None )
        
        self.theme = controller.theme
        self.controller = controller
        
        self.name = PLUGIN_NAME
        self.displayName = PLUGIN_DISPLAY_NAME
        self.description = PLUGIN_DESCRIPTION
        self.authors = PLUGIN_AUTHORS
        self.website = PLUGIN_WEBSITE

        self.config_window = None
       
        self.controller = controller
        self.conversation_manager = self.controller.conversationManager
        self.parser = controller.unifiedParser

        self.config = controller.config
        self.config.readPluginConfig(self.name)
        
        self.received_offline_message_id = 0
        self.receive_message_id = 0
        self.send_message_id = 0
        
        self.period = None
        
        self.url_re = None

        self.players = []
        self.embed = []

        self.check_if_have_gnash()

        if os.name == "nt" or self.have_gnash == False:
            self.show_modes = SHOW_MODES_NT
        else:
            self.show_modes = SHOW_MODES_POSIX

        # config options
        self.show_mode = self.config.getPluginValue(self.name, 'showMode', SHOW_MODE_INSIDE)
        self.show_mode_sending = self.config.getPluginValue(self.name, 'showModeSending', SHOW_MODE_NONE)
        self.backend = self.config.getPluginValue(self.name, 'backend', BACKEND_WEBKIT)

        self.last_action_when_asked = 0
        self.close_conversation_window_id = 0

    def check_if_have_gnash ( self ):
        self.have_gnash = False
        if os.name != "nt":
            out = os.popen("which gnash").read()
            if out == "/usr/bin/gnash\n":
                self.have_gnash = True
    
    def configure( self ):
        '''display a configuration dialog'''      
        show_mode = self.config.getPluginValue(self.name, 'showMode', SHOW_MODE_INSIDE)
        show_mode_sending = self.config.getPluginValue(self.name, 'showModeSending', SHOW_MODE_NONE)
        backend = self.config.getPluginValue(self.name, 'backend', BACKEND_WEBKIT)
        option_list = []

        # name, optionType, label, description, value, options
        option_list.append(Plugin.Option('showMode', list, _('When receiving a video'), \
            _('Change how to show the received video'), show_mode, \
            self.show_modes, self.show_mode_changed))

        option_list.append(Plugin.Option('showModeSending', list, _('When sending a video'), \
            _('Change how to show the sended video'), show_mode_sending, \
            self.show_modes, self.show_mode_sending_changed))

        option_list.append(Plugin.Option('backend', list, _('Backend to display videos'), \
            _('Choose which backend use to show videos'), backend, \
            BACKENDS, self.backend_changed))
        
        self.config_window = Plugin.ConfigWindow( _( 'Config Plugin' ), option_list )
        
        response = self.config_window.run()
        if response != None:
            if response.has_key('showMode'):
                self.show_mode = response['showMode'].value
                self.config.setPluginValue( self.name, 'showMode',
                                            response['showMode'].value )
            if response.has_key('showModeSending'):
                self.show_mode_sending = response['showModeSending'].value
                self.config.setPluginValue( self.name, 'showModeSending',
                                            response['showModeSending'].value )
            if response.has_key('backend'):
                self.backend = response['backend'].value
                self.config.setPluginValue( self.name, 'backend',
                                            response['backend'].value )
        self.config_window = None

        # Shows a warning message if selected backend is not available
        self.check_backend_message(self.backend)
        
        return True
             
    def show_mode_changed( self, combo ):
        pass
        
    def show_mode_sending_changed( self, combo ):
        pass
        
    def backend_changed( self, combo ):
        '''change video display engine'''
        backend = combo.get_active_text()
        self.check_backend_message(backend)
        
    def check_backend_message(self, backend):
        message = None
        if backend == BACKEND_WEBKIT and HAVE_WEBKIT == False:
            message = _("Sorry, webkit (pywebkitgtk) not detected")

        if backend == BACKEND_GECKO and HAVE_GECKO == False:
            message = _("Sorry, gecko (gtkmozembed) not detected")
        
        if message != None:  
            message = message + _("\nPlease install correctly the selected backend or choose another one")
            self.show_warning_message(message)

    def show_warning_message(self, message):
        '''shows a warning dialog with some message'''
        message_box = gtk.MessageDialog(None, \
            gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, \
            gtk.MESSAGE_WARNING, gtk.BUTTONS_OK, \
            message)
        message_box.set_title(_("Error"))
        message_box.run()
        message_box.destroy()
                
    def check( self ):
        '''check if everything is OK to start the plugin
        return a tuple whith a boolean and a message
        if OK -> ( True , 'some message' )
        else -> ( False , 'error message' )'''
        
        if ERROR != '':
            return (False, ERROR)

        if HAVE_WEBKIT is False and HAVE_GECKO is False:
            return(True, _('This plugin works better if you install GTKMozEmbed \
                            (python-gtkmozembed) or Webkit (python-webkit)'))

        return (True, 'Ok')
    
    def start( self ):
        '''start the plugin'''

        self.receive_message_id = self.conversation_manager.connect( \
                                    'receive-message', self.received_message)
        self.close_conversation_window_id = self.conversation_manager.connect(
                                    'close-conversation-ui', self.close_conversation_window)        
        self.send_message_id = self.conversation_manager.connect( \
                                    'send-message', self.send_message)
        self.received_offline_message_id = self.connect( \
                                    'offline-message-received', self.received_offline_message)
        self.init_re()
        
        self.enabled = True

    def stop( self ):
        '''stop the plugin'''
        self.conversation_manager.disconnect(self.receive_message_id)
        self.conversation_manager.disconnect(self.close_conversation_window_id)
        self.conversation_manager.disconnect(self.send_message_id)
        self.disconnect(self.received_offline_message_id)
        
        self.enabled = False

    def send_message(self, cm, conversation, message):
        '''callback form an outgoing message'''
        self.parse_url(conversation, message, True)
        
    def received_message(self, cm, conversation, mail, nick, message, format, charset, p4c):
        '''callback form an incoming message'''
        self.parse_url(conversation, message, False)

    def received_offline_message(self, msnp, oim):
        '''callback from an incoming offline message'''
        user, date, message = oim
        mail = user['addr']
        result = self.conversation_manager.getOpenConversation(mail)
        if result != None:
            conversation = result[1]
        else:
            conversation = None
        self.parse_url(conversation , message, False)
    
    def init_re( self ):
        '''re.compile for detecting youtube links'''	
        urls = '(?: %s)' % '|'.join("""http telnet gopher file wais ftp""".split())
        ltrs = r'\w'
        gunk = r'/#~:.?+=&%@!\-'
        punc = r'.:?\-'
        any = "%(ltrs)s%(gunk)s%(punc)s" % { 'ltrs' : ltrs,
                                            'gunk' : gunk,
                                            'punc' : punc }

        url = r"""
            \b                       # start at word boundary
                %(urls)s    :        # need resource and a colon
                [%(any)s]  +?        # followed by one or more
                                     # of any valid character, but
                                     # be conservative and take only
                                     # what you need to....
            (?=                      # look-ahead non-consumptive assertion
                    [%(punc)s]*      # either 0 or more punctuation
                    (?:   [^%(any)s] # followed by a non-url char
                        |            # or end of the string
                        $
                    )
            )
            """ % {'urls' : urls,
                'any' : any,
                'punc' : punc }

        self.url_re = re.compile(url, re.VERBOSE | re.MULTILINE)

    def parse_url( self, conversation, message, sending ):
        '''split the video id from the URL'''        

        urls = self.url_re.findall(message)

        if urls == []:
            urls = None

        if urls != None:
            url = urls[0] # Just take one

            # only if starts with this >>
            if str(url).startswith('http://www.youtube.com/watch?v='):
                url_parsed = urlparse(url)

                if len(url_parsed[4]) > 0:
                    # If fails, this is not a youtube valid link
                    try:	
                        params = dict([part.split('=') for part in url_parsed[4].split('&')])
                        if len(params) > 0 and len(params['v']) > 0:
                            self.show_the_video( conversation, params['v'], sending )
                    except:
                        return

    def ask_video_show_mode( self ):
        label_question = gtk.Label(_('What do you want to do with the video?'))
        combo = gtk.combo_box_new_text()
        for item in self.show_modes:
            if item != SHOW_MODE_ASK:
                combo.append_text(item)
        
        combo.set_active(self.last_action_when_asked)
        
        show_mode_dialog = gtk.Dialog(_('Flash video detected!'), None, \
            gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, \
                 (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
        show_mode_dialog.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
        show_mode_dialog.set_border_width( 10 )
        show_mode_dialog.set_resizable(False)   
                
        show_mode_dialog.vbox.set_spacing(10)
        show_mode_dialog.vbox.pack_start(label_question)
        show_mode_dialog.vbox.pack_end(combo)
        
        show_mode_dialog.show_all()

        show_mode_dialog.run()
        self.last_action_when_asked = combo.get_active()
        show_mode = combo.get_active_text()
        show_mode_dialog.destroy()
        return show_mode

    def show_video_popup( self, conversation, video_widget, video_frame, video_id, video_title ):
        '''Build a PopUp window and use it as a child of conversation window'''
        if video_widget is None or video_frame is None:
            self.print_message("Can't create video_widget and/or video_frame")
            return

        video_window = gtk.Window()
        
        # Colormap rgba
        video_window.set_colormap(gtk.gdk.colormap_get_system())            

        # when conversation is minimized it's also minimized the video window
        video_window.set_transient_for(conversation.parentConversationWindow)
        
        settings = video_window.get_settings()
        settings.set_property("gtk-button-images", True)

        vbox = gtk.VBox(False, 0) # Video + xbox
        xbox = gtk.HBox(False, 3) # Progressbar + cancel button

        prbar = gtk.ProgressBar()
        prbar.set_size_request(364, 30)
        prbar.set_fraction(0.0)
        prbar.set_text(_('Starting download'))

        prcancel = gtk.Button('X')
        prcancel.set_size_request(36, 30)
        prcancel.set_tooltip_text(_('Stop downloading ') + video_title)
        prcancel.connect('clicked', self.cancel_download, prcancel, prbar)

        dbutton = gtk.Button(None, gtk.STOCK_SAVE)
        self.remove_button_label(dbutton)           
        dbutton.set_tooltip_text(_('Save ') + video_title)
        dbutton.set_size_request(400, 30)
        dbutton.connect('clicked', self.download_video, video_id, conversation, prbar, prcancel, xbox)

        xbox.pack_start(prbar, False)
        xbox.pack_start(prcancel, False)

        video_frame.add(video_widget)
        video_frame.set_size_request(400, 270)
        vbox.pack_start(video_frame, True)

        vbox.pack_end(xbox, False)
        vbox.pack_end(dbutton, False)

        video_window.add(vbox)
        video_window.set_title('Emesene - ' + video_title)
        video_window.show_all()

        prbar.hide() 		# Progressbar
        prcancel.hide()		# Cancel Button
        xbox.hide()			# Box Itself          

    def show_video_inside( self, conversation, video_widget, video_frame, video_id, video_title ):
        '''Change the conversation GUI and put video there'''
        if video_widget is None or video_frame is None:
            self.print_message("Can't create video_widget and/or video_frame")
            return

        settings = conversation.ui.get_settings()
        settings.set_property("gtk-button-images", True)

        obox = gtk.VBox(False, 3)   # all boxes inside
        mbox = gtk.HBox(False, 3)   # buttons + video
        vbox = gtk.HBox(False, 3)   # MBox
        bbox = gtk.VBox(False, 3)   # buttons' box
        xbox = gtk.HBox(False, 3)   # MBox + progressbar + cancel button

        xbox.set_size_request(300, 20) 
        # obox.set_size_request(300,230)

        cb = gtk.Button(None, gtk.STOCK_CLOSE)
        self.remove_button_label(cb)         
        cb.set_tooltip_text(_('Close ') + video_title)
        cb.set_size_request(35, 100)	
        cb.connect('clicked', self.remove_flash, conversation, obox, video_widget)

        prbar = gtk.ProgressBar()
        prbar.set_size_request(264, 15)
        prbar.set_fraction(0.0)
        prbar.set_text(_('Starting download'))

        self.print_message("creating cancel button")
        prcancel = gtk.Button('X')
        prcancel.set_size_request(36, 15)
        prcancel.set_tooltip_text(_('Stop downloading ') + video_title)
        prcancel.connect('clicked', self.cancel_download, prcancel, prbar)
        self.print_message("cancel button created")

        db = gtk.Button(None, gtk.STOCK_SAVE)
        self.remove_button_label(db)            
        db.set_tooltip_text(_('Save ') + video_title)
        db.set_size_request(35, 100)
        db.connect('clicked', self.download_video, video_id, conversation, prbar, prcancel, xbox)
        
        bbox.pack_start( cb, True)          # Close button
        bbox.pack_start( db, True)          # Download button

        video_frame.add(video_widget)
        mbox.pack_start( video_frame, True) # Video

        mbox.pack_start( bbox, False)       # Buttons' Box
        vbox.pack_start( mbox, True)        # Buttons and Video Box
        obox.pack_start( vbox, True)        # Box with buttons and video
        xbox.pack_start( prbar, True)       # Progressbar
        xbox.pack_start( prcancel, False)   # Download-Cancel Button
        obox.pack_start( xbox, False)       # Video, Buttons and Progressbar. AIO.

        conversation.ui.mainBox.pack_start(obox, True, True)

        obox.show_all() # Show all but
        prbar.hide()    # Progressbar
        prcancel.hide() # Cancel Button
        xbox.hide()     # Box Itself
        
        # We save them to be able to destroy them later
        self.players.insert(0, vbox)
        self.embed.insert(0, video_widget)

    def show_the_video( self, conversation, video_id, sending ):
        '''after some checks, build the gui and trigger the video '''	
        if video_id == '':
            # There are some bad urls out there 
            return

        if conversation is None:
            self.print_message("Conversation is none!")
 
        if sending == True:
            show_mode = self.show_mode_sending
        else:     
            show_mode = self.show_mode     
            
        backend = self.backend
        
        self.print_message(_("Using %s as backend") % backend)
                    
        video_url = None
        video_title = ''
        
        if HAVE_GDATA:
            yt_service = gdata.youtube.service.YouTubeService()
            uri = YOUTUBE_VIDEO_URI + '/' + video_id
            entry = yt_service.GetYouTubeVideoEntry(uri)
            video_url = entry.GetSwfUrl()
            video_title = entry.media.title.text
        else:
            self.print_message(_("As gdata is not installed we can't check if we can show the video or not"))
            video_url = "http://www.youtube.com/v/" + video_id
            # also we can't know video's title, so we use it's id
            video_title = video_id
        
        # YouTube have several problems with the world.
        # Videos marked as "Embedding disabled by request" can't be shown.
        # Some videos are not geographically available
        # Some videos require to be logged in
        # Some videos are 18+ aged
        # Some links are youtube channels
        # and some are not even related with youtube :D
        # etc, etc, this causes a None url when using google data (python-gdata)

        if video_url is None:
            return
        
        self.print_message(_("Trying to open ") + video_url)
        
        if show_mode == SHOW_MODE_ASK:
            # The "ask mode" dialog must be created before the webkit view to avoid a segfault
            show_mode = self.ask_video_show_mode()

        if backend == BACKEND_BROWSER:
            self.print_message("With none backend, we only can show the video in a web navigator")
            if show_mode == SHOW_MODE_INSIDE or show_mode == SHOW_MODE_POPUP:
                show_mode = SHOW_MODE_WEB_BROWSER

        # Create video_widget and video_frame (if necessary)

        video_widget = None

        self.print_message("Creating backend %s" % backend)

        try:
            if backend == BACKEND_WEBKIT:
                video_widget = webkit.WebView()
                video_widget.open(video_url)
            elif backend == BACKEND_GECKO:
                video_widget = gtkmozembed.MozEmbed()          
                video_widget.load_url(video_url)
            else:
                video_widget = None
        except:
            self.print_message(_("Error loading the %s backend") % backend)
            conversation.appendOutputText(None, _('\nError while loading video\n'),'error')
            return

        if video_widget is not None or backend is BACKEND_BROWSER:
            self.print_message("Backend %s created correctly" % backend)

        video_frame = None
        
        if backend == BACKEND_WEBKIT:
            video_frame = gtk.ScrolledWindow()
        elif backend == BACKEND_GECKO:
            video_frame = gtk.Frame()
        else:
            video_frame = None

        if show_mode == SHOW_MODE_POPUP:
            self.print_message(_('Showing the video in a popup window'))
            self.show_video_popup(conversation, video_widget, video_frame, video_id, video_title)
        
        elif show_mode == SHOW_MODE_INSIDE:
            self.print_message(_('Showing the video inside the conversation window'))
            self.show_video_inside(conversation, video_widget, video_frame, video_id, video_title)

        elif show_mode == SHOW_MODE_SAVE:
            self.print_message(_('Don\'t show the video, just download it'))
            
            settings = conversation.ui.get_settings()
            settings.set_property("gtk-button-images", True)

            # Show the progressbar as well
            xbox = gtk.HBox(False, 3)   # MBox + progressbar + cancel button

            xbox.set_size_request(300, 20) 
            prbar = gtk.ProgressBar()
            prbar.set_size_request(264, 15)
            prbar.set_fraction(0.0)
            prbar.set_text(_('Starting download'))

            prcancel = gtk.Button('X')
            prcancel.set_size_request(36, 15)
            prcancel.set_tooltip_text(_('Stop downloading ') + video_title)
            prcancel.connect('clicked', self.cancel_download, prcancel, prbar)
            
            xbox.pack_start( prbar, True)       # Progressbar
            xbox.pack_start( prcancel, False)   # Download-Cancel Button

            conversation.ui.mainBox.pack_start(xbox, False)
            
            prbar.hide()    # Progressbar
            prcancel.hide() # Cancel Button
            xbox.hide()     # Box Itself

            self.download_video(None, video_id, conversation, prbar, prcancel, xbox)
        elif show_mode == SHOW_MODE_WEB_BROWSER:
            self.print_message(_('Show the video in a web browser'))
            
            try:
                if os.name != "nt":
                    retval = subprocess.call(['firefox',  video_url])
                else:
                    desktop.open(video_url)
            except OSError, e:
                pass

    def remove_button_label(self, button):
        '''remove the label, leaving the gtk_stock icon only'''	
        alignment = button.get_children()[0]
        hboxtemp = alignment.get_children()[0]
        image, text = hboxtemp.get_children()
        text.set_text('')

    def remove_flash(self, widget, conv, flv, player):
        '''remove box form conversation window'''
        player.destroy()
        conv.ui.mainBox.remove(flv)

    def close_conversation_window(self, conversation_manager, conversation, win):
        '''remove all flashes, boxes, and so when a conversation is close, 
        this prevents random crashes so far'''

        for widget in self.players:
            try:
                # kill all open players when we close the conversation window
                # but only those of the current conversation
                conversation.widget.destroy()
            except:
                pass

        for item in self.embed:
            try:
                conversation.ui.mainBox.remove(item)
            except:
                pass

    def download_video(self, button, video, conv, progress_bar=None, c_button=None, box=None):
        '''Get ready, this launch youtube_dl class for downloading videoPlugin.Function
        Parameters with None are optional'''

        youtube_dl = Youtube_dl()
        youtube_dl.start_download(video, conv, progress_bar, c_button, box)

        if progress_bar is not None:
            box.show_all() # Show the box with the Bar and Button

    def cancel_download(self, widget, data, progressbar):
        ''' As the downloading part is in another class, we change the button 
        label. It will be compared during the download, so it'll be canceled'''

        # Ugly Fix
        try:
            widget.set_label(".")
            progressbar.set_text(_('Cancelling...'))
        except:	
            pass
            
    def print_message(self, message):
        if PLUGIN_DEBUG == 1:
            print "YAYOUTUBE: " + message


# ------------------------------------------------------------------------------        
# Class used to download a youtube video
# Mainly extracted from youtube-dl
# Put in a new class, this way will be easier
# to update if a new version of youtube-dl shows up

class Youtube_dl:

    def __init__(self):
        pass

    def start_download (self, video, conversation, progress_bar=None, button=None, cbox=None):
        '''We use threads so emesene can work normally and don't get stuck.
        Again, progressbar, button and cbox are option'''

        thread.start_new_thread(self.download_the_video, (video, conversation, progress_bar, button, cbox))
    

    def youtube_dl_extract_step(self, step_title, step_error, regexp, data):
        '''Generic extract step'''

        match = regexp.search(data)

        if match is None:
            return None

        extracted_data = match.group(1)
        return extracted_data
    
    def youtube_dl_download_step(self, return_data_flag, step_title, step_error, url, post_data=None):
        '''Generic download step'''

        try:
            data = self.youtube_dl_perform_request(url, post_data).read()
            if return_data_flag:
                return data
            return None

        except (urllib2.URLError, ValueError, httplib.HTTPException, TypeError, socket.error):
            return None

    
    def youtube_dl_request_create(self, url, data=None):
        '''Wrapper to create custom requests with typical headers
        Try to mimic Firefox, at leat a little bit'''

        retval = urllib2.Request(url)
        if data is not None:
            retval.add_data(data)
        
        retval.add_header('User-Agent', 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11')
        retval.add_header('Accept-Charset', 'ISO-8859-1,utf-8;q=0.7,*;q=0.7')
        retval.add_header('Accept', 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5')
        retval.add_header('Accept-Language', 'en-us,en;q=0.5')
        return retval

    
    def youtube_dl_perform_request(self, url, data=None):
        '''Perform a request, process headers and return response'''

        request = self.youtube_dl_request_create(url, data)
        response = urllib2.urlopen(request)
        return response

    
    def youtube_dl_new_block_size(self, before, after, bytes):
        '''Calculate new block size based on previous block size'''

        new_min = max(bytes / 2.0, 1.0)
        new_max = max(bytes * 2.0, 1.0)
        dif = after - before
        if dif < CONST_EPSILON:
            return int(new_max)
        rate = bytes / dif
        if rate > new_max:
            return int(new_max)
        if rate < new_min:
            return int(new_min)
        return int(rate)
    
    def show_message(self, conv, msg, type='error'):
        '''Show message in conversation
        Error is default'''

        try:
            conv.appendOutputText(None, msg, type)
        except:
            pass

   
    def download_the_video(self, video_url_cmdl, conv, progress_bar=None, button=None, box=None):
        '''Main function that downloads the video and stores it'''
        
        # Set socket timeout
        socket.setdefaulttimeout(CONST_TIMEOUT)
                
        # Verify video URL format and convert to "standard" format
        video_url_mo = CONST_VIDEO_URL_RE.match(video_url_cmdl)
        if video_url_mo is None:
            # Error: URL does not seem to be a youtube video URL. If it is, report a bug.
            self.show_message(conv, _('Video download failed'))
            if box:
                box.hide()
            return

        video_url_id = video_url_mo.group(2)
        video_url = CONST_VIDEO_URL_STR % video_url_id

        # Save Dialog

        global file
        dialog = gtk.FileChooserDialog(_("Save video as.."),
                                       None,
                                       gtk.FILE_CHOOSER_ACTION_SAVE,
                                       (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                       gtk.STOCK_SAVE, gtk.RESPONSE_OK))
        dialog.set_default_response(gtk.RESPONSE_OK)

        filter = gtk.FileFilter()
        filter.set_name(_("Flash Videos"))
        filter.add_pattern("*.flv")

        dialog.add_filter(filter)
        dialog.show()
        response = dialog.run()
        if response == gtk.RESPONSE_OK:
            video_filename = dialog.get_filename()
            dialog.destroy()
        else:	
            dialog.destroy()
            self.show_message(conv, _('Video download canceled'))
            if box:
                box.hide()
            return

        # Check if ends with '.flv'... add it or not
        nametemp =  video_filename.lower() #can't be persistent 'cause *nix is case sensitive
        if nametemp.endswith('.flv') == False:
            video_filename = video_filename + '.flv'

        t_start = time.strftime("%H:%M:%S", time.localtime())
        self.show_message(conv, _('Video download started [%s]' % t_start),'information')

        # Retrieve video webpage
        video_webpage = self.youtube_dl_download_step(True, 'Retrieving video webpage', 'unable to retrieve video webpage', video_url)
        if video_webpage == None:
            self.show_message(conv, _('Video download failed'))
            if box:
                box.hide()
            return
        
        # Extract needed video URL parameters
        video_url_t_param = self.youtube_dl_extract_step('Extracting URL "t" parameter', 'unable to extract URL "t" parameter', CONST_URL_T_PARAM_RE, video_webpage)
        if video_url_t_param == None:
            self.show_message(conv, _('Video download failed'))
            if box:
                box.hide()
            return

        video_url_real = CONST_VIDEO_URL_REAL_STR % (video_url_id, video_url_t_param)
        
        # Retrieve video data
        try:
            video_data = self.youtube_dl_perform_request(video_url_real)

            try:
                video_file = open(video_filename, 'wb')
            except (IOError, OSError):
                self.show_message(_('Video download failed. Cannot open %s for I/O' % video_filename))
                if box:
                    box.hide()
                return

            byte_counter = 0
            block_size = CONST_INITIAL_BLOCK_SIZE

            std_headers = {
                'User-Agent': 'Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2',
                'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
                'Accept': 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5',
                'Accept-Language': 'en-us,en;q=0.5',
            }

            request = urllib2.Request(video_url_real, None, std_headers)

            data = urllib2.urlopen(request)
            data_len = data.info()['Content-Length']

            fname = os.path.basename(video_filename) #give the filename and not the path

            canceled = False

            while True:
                if button.get_label() == ".": #check for canceled button pressed signal
                    canceled = True
                    button.hide()
                    break
                before = time.time()
                video_block = video_data.read(block_size)
                after = time.time()
                dl_bytes = len(video_block)
                if dl_bytes == 0:
                    break
                byte_counter += dl_bytes
                video_file.write(video_block)
                block_size = self.youtube_dl_new_block_size(before, after, dl_bytes)	
                if progress_bar:
                    # Progress_bar values are:
                    # 0 - empty 0%
                    # 1 - full 100%
                    # byte_counter is the progress over data_len
                    # Do the maths!

                    porc = '%s' % ('%.1f' % (float(byte_counter) / float(data_len) * 100.0))
                    f = '%.1f' % (float(porc) / 100)

                    progress_bar.set_text(_('Downloading %s (%s%% of %.2f MB)' % ( fname, porc, float(data_len) / 1048576 ) ))
                    progress_bar.set_fraction(float(f))

            if progress_bar:
                if canceled == False:
                    # Sometimes the progress freezes at 99.X... so force it to 100%
                    progress_bar.set_fraction(1.0)
                    progress_bar.set_text(_('%s downloaded successfully!' % fname))
                else:
                    button.set_label("X")
                button.hide()

            video_file.close()

            t_end = time.strftime("%H:%M:%S", time.localtime())
            if not canceled:
                self.show_message(conv, _('Video saved in %s [%s]') % (video_filename, t_end),'information')
            else:
                self.show_message(conv, _('%s download canceled') % fname)

            if box:
                progress_bar.set_text(_('Starting download'))
                progress_bar.set_fraction(0.0)
                box.hide()

        except (urllib2.URLError, ValueError, httplib.HTTPException, TypeError, socket.error):
            self.show_message(conv, _('Video download failed'))
            if box:
                box.hide()
            return

